package middleware

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	log "github.com/sirupsen/logrus"
	"io/ioutil"
	"os"
	"strings"
	"time"
)

type bodyWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (w bodyWriter) Write(b []byte) (int, error) {
	w.body.Write(b) // 将响应写入缓冲区
	return w.ResponseWriter.Write(b)
}

var (
	Red    = "\033[31m"
	Yellow = "\033[33m"
	Blue   = "\033[34m"
	Reset  = "\033[0m"
)

type ColorJSONFormatter struct{}

func (f *ColorJSONFormatter) Format(entry *log.Entry) ([]byte, error) {
	b := &bytes.Buffer{}

	// 设置颜色
	color := Reset
	switch entry.Level {
	case log.ErrorLevel:
		color = Red
	case log.WarnLevel:
		color = Yellow
	case log.InfoLevel:
		color = Reset
	}

	data, err := json.Marshal(entry.Data)
	if err != nil {
		return nil, err
	}

	fmt.Fprintf(b, "%s%s%s\n", color, data, Reset)
	return b.Bytes(), nil
}

type OpLogger struct {
	log.Logger
}

var Logger *OpLogger

func OpLog() gin.HandlerFunc {
	initOpLogger()
	return func(c *gin.Context) {
		start := time.Now()
		mapAttr := make(map[string]interface{}, 0)
		mapAttr["method"] = c.Request.Method
		mapAttr["time"] = time.Now().Format("2006-01-02 15:04:05")
		mapAttr["path"] = c.Request.URL.Path
		mapAttr["client_ip"] = c.ClientIP()
		mapAttr["request_id"] = c.MustGet("request_id")
		mapAttr["level"] = "info"

		Authorization := c.GetHeader("Authorization")
		claims, err := ParseToken(Authorization)
		if err != nil {
			mapAttr["user_id"] = int64(0)
			mapAttr["user_name"] = "-"
			mapAttr["real_name"] = "-"
		} else {
			mapAttr["user_id"] = claims.UserId
			mapAttr["user_name"] = claims.Username
			mapAttr["real_name"] = claims.RealName
		}
		c.Set("Operator", mapAttr["user_name"])
		c.Set("OperatorName", mapAttr["real_name"])

		bodyBytes, _ := ioutil.ReadAll(c.Request.Body)

		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
		bw := &bodyWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
		c.Writer = bw

		c.Next()
		mapAttr["status"] = c.Writer.Status()
		if mapAttr["status"].(int) != 200 {
			mapAttr["response"] = bw.body.String()
			mapAttr["level"] = "error"
		}
		duration := time.Since(start)
		mapAttr["latency"] = duration.String()
		Logger.LogWithFields(mapAttr, bodyBytes)
	}
}

func (l *OpLogger) LogWithFields(attrs map[string]interface{}, body []byte) {
	getOpAttr(attrs, body)
	fields := log.Fields{}
	for k, v := range attrs {
		fields[k] = v
	}
	entry := l.WithFields(fields)
	if attrs["status"].(int) == 200 {
		entry.Info("---request---")
	} else {
		entry.Error("---request---")
	}
}

func getOpAttr(attrs map[string]interface{}, body []byte) {
	pathParts := strings.Split(attrs["path"].(string), "/")
	action := "unknown"
	if len(pathParts) > 3 {
		action = getActionByReg(pathParts[3])
	}

	attrs["module"] = "iot"
	attrs["action"] = action
	bodyMap1 := make(map[string]interface{})
	bodyMap2 := make([]map[string]interface{}, 0)
	err := json.Unmarshal(body, &bodyMap1)
	if err != nil {
		_ = json.Unmarshal(body, &bodyMap2)
		attrs["details"] = bodyMap2
	} else {
		attrs["details"] = bodyMap1
	}
}

func getActionByReg(path string) string {
	switch {
	case strings.HasPrefix(path, "refreshToken") || path == "getCaptcha":
		return "刷新"
	case strings.HasPrefix(path, "get") || strings.HasPrefix(path, "find") || strings.HasPrefix(path, "recheckLimsItem") || strings.HasPrefix(path, "imageView") || strings.HasPrefix(path, "viewU9Status") || path == "captcha":
		return "查询"
	case strings.HasPrefix(path, "add") || strings.HasPrefix(path, "save") || strings.HasPrefix(path, "prodWhAction") || strings.HasPrefix(path, "meterReport"):
		return "添加"
	case strings.HasPrefix(path, "del"):
		return "删除"
	case strings.HasPrefix(path, "upd"):
		return "更新"
	case strings.HasPrefix(path, "import"):
		return "导入"
	case strings.HasPrefix(path, "export"):
		return "导出"
	case strings.HasPrefix(path, "search"):
		return "检索"
	case strings.HasPrefix(path, "close"):
		return "关闭"
	case strings.HasPrefix(path, "upload"):
		return "上传"
	case strings.Contains(path, "review") || strings.Contains(path, "Review") || strings.Contains(path, "returnQaIllegalOperation"):
		return "审核"
	case strings.HasPrefix(path, "login") || strings.HasPrefix(path, "feiShuLogin"):
		return "登录"
	default:
		return "unknown action"
	}
}

func initOpLogger() {
	Logger = &OpLogger{}
	Logger.SetFormatter(&ColorJSONFormatter{})
	Logger.SetOutput(os.Stdout)
	Logger.SetLevel(log.DebugLevel)
}
